/*
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2003-2008 Intel Corporation. All Rights Reserved.
//
//
*/
#include "umc_defs.h"
#if defined (UMC_ENABLE_DV_VIDEO_DECODER)

#include <math.h>
#include <ippi.h>
#include <ipps.h>
#include <ippvc.h>
#include "vm_event.h"
#include "vm_thread.h"
#include "vm_sys_info.h"
#include "umc_dv_decoder.h"
#include "umc_dv_internal.h"
#include "umc_video_data.h"
#include "umc_memory_allocator.h"

#ifdef _OPENMP
#include "omp.h"
#endif

namespace UMC
{

VideoDecoder *CreateDVDecoder() { return new DVVideoDecoder(); }

DVVideoDecoder::DVVideoDecoder()
{
    StoreDVSegment      = NULL;

    m_nSystem           = SYSTEM_UNK;
    m_nPitch            = 0;
    m_lSizeSubSampled   = 0;
    m_bDCOnly           = false;
    m_bInitSuccess      = false;

    m_nNumberOfThreads  = 0;
    vm_event_set_invalid(&m_Quit);

    m_lpThreads         = NULL;
    m_lpStartEvent      = NULL;
    m_lpStopEvent       = NULL;
    m_lpThreadsID       = NULL;

    // pointer to hold allocated memory
    m_pHuffTable        = NULL;
    m_ppShortBlocks     = NULL;

    m_lpADequantizeLineTable            = NULL;

    // working pointer
    m_lpADequantizeTable = NULL;
    m_lpvInternalFrameBuffer = NULL;

    m_InternalFrameBufferMID = 0;
    m_DCTBlocksBufferMID = 0;
    m_DequantizeTableMID = 0;
} // DVVideoDecoder::DVVideoDecoder()

DVVideoDecoder::~DVVideoDecoder()
{
    Close();

} // DVVideoDecoder::~DVVideoDecoder()

Status DVVideoDecoder::Close()
{
    Ipp32u i;

    // let all threads to exit
    if (m_lpThreads)
    {
        vm_event_signal(&m_Quit);

        for (i = 1;i < m_nNumberOfThreads;i += 1)
            vm_event_signal(m_lpStartEvent + i);

        for (i = 1;i < m_nNumberOfThreads;i += 1)
        {
            if (vm_thread_is_valid(m_lpThreads + i))
                vm_thread_wait(m_lpThreads + i);
        }
    }

    // delete all threading tools
    if (vm_event_is_valid(&m_Quit))
    {
        vm_event_destroy(&m_Quit);
        vm_event_set_invalid(&m_Quit);
    }

    if (m_lpThreads)
    {
        for (i = 1;i < m_nNumberOfThreads;i += 1)
            vm_thread_set_invalid(m_lpThreads + i);

        delete [] m_lpThreads;
        m_lpThreads = NULL;
    }

    if (m_lpStartEvent)
    {
        for (i = 1;i < m_nNumberOfThreads;i += 1)
        {
            if (vm_event_is_valid(m_lpStartEvent + i))
            {
                vm_event_destroy(m_lpStartEvent + i);
                vm_event_set_invalid(m_lpStartEvent + i);
            }
        }

        delete [] m_lpStartEvent;
        m_lpStartEvent = NULL;
    }

    if (m_lpStopEvent)
    {
        for (i = 1;i < m_nNumberOfThreads;i += 1)
        {
            if (vm_event_is_valid(m_lpStopEvent + i))
            {
                vm_event_destroy(m_lpStopEvent + i);
                vm_event_set_invalid(m_lpStopEvent + i);
            }
        }

        delete [] m_lpStopEvent;
        m_lpStopEvent = NULL;
    }

    if (m_lpThreadsID)
    {
        delete [] m_lpThreadsID;
        m_lpThreadsID = NULL;
    }

    if (m_pHuffTable)
    {
        ippiFreeHuffmanTable_DV_32u((Ipp32u *)m_pHuffTable);
        m_pHuffTable = NULL;
    }

    if(m_ppShortBlocks)
    {
        delete [] m_ppShortBlocks;
        m_ppShortBlocks = NULL;
    }

    if( m_pMemoryAllocator )
    {
        if(m_DCTBlocksBufferMID != 0)
        {
            m_pMemoryAllocator->Free(m_DCTBlocksBufferMID);
            m_DCTBlocksBufferMID = 0;
        }

        if(m_InternalFrameBufferMID != 0)
        {
            m_pMemoryAllocator->Free(m_InternalFrameBufferMID);
            m_InternalFrameBufferMID = 0;
        }

        if(m_DequantizeTableMID != 0)
        {
            m_pMemoryAllocator->Free(m_DequantizeTableMID);
            m_DequantizeTableMID = 0;
        }
    }

    if (m_lpADequantizeLineTable)
    {
        m_lpADequantizeLineTable = NULL;
    }

    BaseCodec::Close(); // delete own allocator

    m_nSystem = SYSTEM_UNK;

    m_bDCOnly = false;

    m_nPitch = 0;

    StoreDVSegment = NULL;

    // working pointer
    m_lpADequantizeTable = NULL;
    m_lpvInternalFrameBuffer = NULL;

    return UMC_OK;

} // Status DVVideoDecoder::Close()


Status DVVideoDecoder::Init(BaseCodecParams *lpInit)
{
    VideoDecoderParams *init_param = DynamicCast<VideoDecoderParams> (lpInit);
    Ipp32u i;

    if (NULL == init_param)
        return UMC_ERR_NULL_PTR;

    Close();

    if (UMC_OK != BaseCodec::Init(lpInit)) // creates memory allocator if needed
        return UMC_ERR_INIT;

    if(init_param->numThreads == 0)
    {
#ifdef _OPENMP
        m_nNumberOfThreads = omp_get_max_threads();
#else //_OPENMP
        m_nNumberOfThreads = vm_sys_info_get_cpu_num();
#endif //_OPENMP
    }
    else
        m_nNumberOfThreads = init_param->numThreads;


    if (0 >= m_nNumberOfThreads)
        m_nNumberOfThreads = 1;
    else if (8 < m_nNumberOfThreads)
        m_nNumberOfThreads = 8;

    // allocate working buffer(s)
    if( m_pMemoryAllocator->Alloc(&m_DCTBlocksBufferMID,
                                  SIZE_OF_VIDEO_SEGMENT * m_nNumberOfThreads,
                                  UMC_ALLOC_PERSISTENT,
                                  16) != UMC_OK )
        return UMC_ERR_ALLOC;

    m_ppShortBlocks = new Ipp16u* [m_nNumberOfThreads];
    if (NULL == m_ppShortBlocks)
        return UMC_ERR_ALLOC;
    memset(m_ppShortBlocks, 0, sizeof(Ipp16u*) * m_nNumberOfThreads);

    // create threading tools
    if (1 < m_nNumberOfThreads)
    {
        // create stop event
        if (VM_OK != vm_event_init(&m_Quit, 1, 0))
            return UMC_ERR_INIT;

        // create start event(s)
        m_lpStartEvent = new vm_event[m_nNumberOfThreads];
        if (NULL == m_lpStartEvent)
            return UMC_ERR_ALLOC;
        for (i = 1;i < m_nNumberOfThreads;i += 1)
        {
            vm_event_set_invalid(m_lpStartEvent + i);
            if (VM_OK != vm_event_init(m_lpStartEvent + i, 0, 0))
                return UMC_ERR_INIT;
        }

        // create stop event(s)
        m_lpStopEvent = new vm_event[m_nNumberOfThreads];
        if (NULL == m_lpStopEvent)
            return UMC_ERR_ALLOC;
        for (i = 1;i < m_nNumberOfThreads;i += 1)
        {
            vm_event_set_invalid(m_lpStopEvent + i);
            if (VM_OK != vm_event_init(m_lpStopEvent + i, 0, 0))
                return UMC_ERR_INIT;
        }

        // allocate thread ID(s)
        m_lpThreadsID = new THREAD_ID[m_nNumberOfThreads];
        if (NULL == m_lpThreadsID)
            return UMC_ERR_ALLOC;

        // starting thread(s)
        m_lpThreads = new vm_thread[m_nNumberOfThreads];
        if (NULL == m_lpThreads)
            return UMC_ERR_ALLOC;
        for (i = 1;i < m_nNumberOfThreads;i += 1)
        {
            vm_thread_set_invalid(m_lpThreads + i);
            m_lpThreadsID[i].m_nNumber = i;
            m_lpThreadsID[i].m_lpOwner = this;
            if (0 == vm_thread_create(m_lpThreads + i, ThreadWorkingRoutine, m_lpThreadsID + i))
                return UMC_ERR_INIT;
        }
    }

    m_nHeight = init_param->info.clip_info.height;
    m_nWidth = init_param->info.clip_info.width;

    // check parameters
    if ((WIDTH_DV != m_nWidth) || ((HEIGHT_625 != m_nHeight) && (HEIGHT_525 != m_nHeight)))
        return UMC_ERR_INVALID_STREAM;

    // set working system
    m_nSystem = ((HEIGHT_625 == m_nHeight) ? (SYSTEM_625) : (SYSTEM_525));
    m_nMaxNumberOfDIFSequences = ((HEIGHT_625 == m_nHeight) ? (12) : (10));
    if (SYSTEM_525 == m_nSystem) {
        m_nSourceFrameSize = 120000;
    } else  {
        m_nSourceFrameSize = 144000;
    }

    m_PostProcessing = init_param->pPostProcessing;

    m_ClipInfo = init_param->info;
    m_ClipInfo.clip_info.width = m_nWidth;
    m_ClipInfo.clip_info.height = m_nHeight;

    // allocate internal frame buffer
    {
        m_nPitch = align_value<Ipp32u> (2 * m_nWidth, WIDTH_ALIGN);

        if( m_pMemoryAllocator->Alloc(&m_InternalFrameBufferMID,
                                      m_nPitch * m_nHeight,
                                      UMC_ALLOC_PERSISTENT,
                                      16) != UMC_OK )
        return UMC_ERR_ALLOC;
    }

    // allocate buffer(s)
    if (ippStsOk != ippiInitAllocHuffmanTable_DV_32u((Ipp32s *) dvTable1,
                                                     (Ipp32s *) dvTable2,
                                                     reinterpret_cast<Ipp32u **> (&m_pHuffTable)))
        return UMC_ERR_ALLOC;

    if (DVCreateADequantizeTable())
        return UMC_ERR_ALLOC;

    m_bInitSuccess = true;

    return UMC_OK;
}

void DVVideoDecoder::CheckSetCorrectParams(int byteDSF)
{
    if ( byteDSF == 0x00)               //NTSC
    {
        m_ClipInfo.clip_info.height = 480;
        m_nHeight = 480;
        m_nSourceFrameSize = 120000;
        m_nMaxNumberOfDIFSequences = 10;
    }
    else if (byteDSF == 0x080)          //PAL
    {
        m_ClipInfo.clip_info.height = 576;
        m_nHeight = 576;
        m_nSourceFrameSize = 144000;
        m_nMaxNumberOfDIFSequences = 12;
    }
}
void DVVideoDecoder::SelectStoreFunction(void *pSource)
{
    Ipp8u *lp = reinterpret_cast<Ipp8u *> (pSource);
    Ipp32u nStreamType = DVSD_STREAM;
    Ipp8u APT = (*(lp+4)) & 0x07;
    Ipp8u AP1 = (*(lp+5)) & 0x07;
    Ipp8u AP2 = (*(lp+6)) & 0x07;
    Ipp8u AP3 = (*(lp+7)) & 0x07;
    // check error(s)
    if ((APT == AP1) &&
        (AP2 == AP3) &&
        (APT == AP3))
    {
      // check real stream type
      switch (APT)
      {
        // Consumer digital VCR
      case 0:
        nStreamType = DVSD_STREAM;
        break;

        // DVCPRO VCR
        //case 1:
      default:
        nStreamType = DV25_STREAM;
        break;
      }
    }

    // set right function
    switch (nStreamType)
    {
    case DVSD_STREAM:
        switch (m_lSizeSubSampled)
        {
        case 2:
            StoreDVSegment = &DVVideoDecoder::StoreDVSDSegment_2s;
            break;

        case 4:
            StoreDVSegment = &DVVideoDecoder::StoreDVSDSegment_4s;
            break;

        case 8:
            StoreDVSegment = &DVVideoDecoder::StoreDVSDSegment_8s;
            break;

        default:
            StoreDVSegment = &DVVideoDecoder::StoreDVSDSegment;
            break;
        }
        break;

    case DV25_STREAM:
        switch (m_lSizeSubSampled)
        {
        case  2:
            StoreDVSegment = &DVVideoDecoder::StoreDV25Segment_2s;
            break;

        case 4:
            StoreDVSegment = &DVVideoDecoder::StoreDV25Segment_4s;
            break;

        case 8:
            StoreDVSegment = &DVVideoDecoder::StoreDV25Segment_8s;
            break;

        default:
            StoreDVSegment = &DVVideoDecoder::StoreDV25Segment;
            break;
        }
        break;
    }
}

Status  DVVideoDecoder::GetFrame(MediaData *pInData, MediaData *pOutData)
{
    VideoData *lpVData = DynamicCast<VideoData> (pOutData);
    Status status = UMC_OK;
    Ipp32u i;

    if (!m_bInitSuccess)
        return UMC_ERR_NOT_INITIALIZED;

    if (NULL == lpVData)
        return UMC_ERR_NULL_PTR;

    if ((NULL == pInData))
    {
        return UMC_ERR_NOT_ENOUGH_DATA;
    }

    if (120000 > pInData->GetDataSize())
    {
        pInData->MoveDataPointer(pInData->GetDataSize());
        return UMC_ERR_NOT_ENOUGH_DATA;
    }

    if (pInData->GetDataSize() < m_nSourceFrameSize)
    {
        //May be error in codec initialization
        //For example: Init() called for NTSC (720x480), but stream in PAL (720x576)
        Ipp8u *pCurrFrame;
        Ipp32s byteDSF;
        pCurrFrame = reinterpret_cast<Ipp8u *> (pInData->GetDataPointer());
        //DSF is situated in each DIF sequence, in header section, in first DIF block - header block
        //DSF = "0x00" NTSC ; DSF = "0x80" PAL
        byteDSF = pCurrFrame[3] & 0x080;
        CheckSetCorrectParams((int) byteDSF);
    }

    m_bDCOnly = false;

    m_lSizeSubSampled = 1;
    Ipp64f widthRatio = m_nWidth/(Ipp64f)lpVData->GetWidth();
    Ipp64f heightRatio = m_nHeight/(Ipp64f)lpVData->GetHeight();
    if (widthRatio == heightRatio) {
      if (widthRatio == 2) {
        m_lSizeSubSampled = 2;
      } else if (widthRatio == 4) {
        m_lSizeSubSampled = 4;
      } else if (widthRatio == 8) {
        m_lSizeSubSampled = 8;
        m_bDCOnly = true;
      }
    }
    Ipp32s dstWidth = m_nWidth/m_lSizeSubSampled;
    Ipp32s dstHeight = m_nHeight/m_lSizeSubSampled;

    // select frame type and set right function
    SelectStoreFunction(pInData->GetDataPointer());

    if (!m_PostProcessing) {
      m_PostProcessing = m_allocatedPostProcessing = createVideoProcessing();
    }

    if (m_allocatedPostProcessing /* post processing is default */ &&
        YUY2 == lpVData->GetColorFormat() &&
        dstWidth == lpVData->GetWidth() &&
        dstHeight == lpVData->GetHeight())
    {
      m_lpDestination = reinterpret_cast<Ipp8u *> (lpVData->GetPlanePointer(0));
      m_nPitch = lpVData->GetPlanePitch(0);
    } else {
      m_lpvInternalFrameBuffer = (Ipp8u*) m_pMemoryAllocator->Lock(m_InternalFrameBufferMID);
      if (NULL == m_lpvInternalFrameBuffer)
           return UMC_ERR_NULL_PTR;

      m_lpDestination = m_lpvInternalFrameBuffer;
      m_nPitch = align_value<Ipp32u> (2 * m_nWidth, WIDTH_ALIGN);
    }

    m_lpSource = reinterpret_cast<Ipp8u *> (pInData->GetDataPointer());

    Ipp8u *pShortBlocks = (Ipp8u*)m_pMemoryAllocator->Lock(m_DCTBlocksBufferMID);
    if (NULL == pShortBlocks)
        return UMC_ERR_NULL_PTR;
    for (i = 0;i < m_nNumberOfThreads;i += 1)
        m_ppShortBlocks[i] = (Ipp16u *) (pShortBlocks + SIZE_OF_VIDEO_SEGMENT * i);

    m_lpADequantizeTable = (Ipp16u*)m_pMemoryAllocator->Lock(m_DequantizeTableMID);
    if(m_lpADequantizeTable == NULL)
        return UMC_ERR_NULL_PTR;

    // start additional thread(s)
    for (i = 1;i < m_nNumberOfThreads;i += 1)
        vm_event_signal(m_lpStartEvent + i);

    DecompressSegment(0);

    // wait additional thread(s)
    for (i = 1;i < m_nNumberOfThreads;i += 1)
        vm_event_wait(m_lpStopEvent + i);

    m_pMemoryAllocator->Unlock(m_DCTBlocksBufferMID);
#ifdef _DEBUG
    memset(m_ppShortBlocks, 0, sizeof(Ipp16u*) * m_nNumberOfThreads);
#endif
    m_pMemoryAllocator->Unlock(m_DequantizeTableMID);
    m_lpADequantizeTable = NULL;

    lpVData->SetFrameType(I_PICTURE);
    lpVData->SetTime(pInData->GetTime());

    if (m_LastDecodedFrame.GetColorFormat() != YUY2 ||
        m_LastDecodedFrame.GetWidth() != dstWidth ||
        m_LastDecodedFrame.GetHeight() != dstHeight)
    {
      m_LastDecodedFrame.Init(dstWidth,
                              dstHeight,
                              YUY2);
    }
    m_LastDecodedFrame.SetFrameType(I_PICTURE);
    m_LastDecodedFrame.SetTime(pInData->GetTime());
    m_LastDecodedFrame.SetPlanePointer((void*)m_lpDestination, 0);
    m_LastDecodedFrame.SetPlanePitch(m_nPitch, 0);
    if (m_lpDestination == m_lpvInternalFrameBuffer)
    {
        // for fix of color conversion issues
        if (lpVData->GetColorFormat() == YV12)
            lpVData->SetColorFormat(YUV420);
        else if (lpVData->GetColorFormat() == YUV420)
            lpVData->SetColorFormat(YV12);
        //
        status = m_PostProcessing->GetFrame(&m_LastDecodedFrame, lpVData);
        m_pMemoryAllocator->Unlock(m_InternalFrameBufferMID);
        m_lpvInternalFrameBuffer = NULL;
        if (lpVData->GetColorFormat() == YV12)
            lpVData->SetColorFormat(YUV420);
        else if (lpVData->GetColorFormat() == YUV420)
            lpVData->SetColorFormat(YV12);
    }

    pInData->MoveDataPointer(m_nSourceFrameSize);
    if (SYSTEM_525 == m_nSystem) {
        pInData->SetTime(pInData->GetTime() + 0.0333666);
    } else  {
        pInData->SetTime(pInData->GetTime() + 1.0/25);
    }

    return status;

} // Status  DVVideoDecoder::GetFrame(MediaData *pInData, MediaData *pOutData)

void DVVideoDecoder::DecompressSegment(Ipp32u nThreadNum)
{
    Ipp32u i, k;
    Ipp32u i_start, i_stop;
    Ipp32u nTemp;
    Ipp8u *lpSrc;

    // check error(s)
    if (nThreadNum >= m_nNumberOfThreads)
        return;

    // set working i & k
    nTemp = m_nMaxNumberOfDIFSequences;
    i_start = (nTemp * (nThreadNum)) / m_nNumberOfThreads;
    i_stop = (nTemp * (nThreadNum + 1)) / m_nNumberOfThreads;

    Ipp16u *lpsBlocks = m_ppShortBlocks[nThreadNum];

    if (false == m_bDCOnly)
    {
        Ipp32u BlParamBuffer[30];
        Ipp32u b_num, mb_num, block_type, block_quant_class, qno, bl_index, eob;
        Ipp16u *lpsTable;

        for (i = i_start;i < i_stop;i += 1)
        {
            for (k = 0;k < 27;k += 1)
            {
                // get source pointer
                lpSrc = m_lpSource + SIZE_OF_DV_SEGMENT * (i * 150 + 6 + (k / 3 + 1) + k * 5);

                //reset working block
                memset(lpsBlocks, 0, SIZE_OF_VIDEO_SEGMENT);

                // start video decompressing
                if(m_lSizeSubSampled == 2)
                    ippiHuffmanDecodeSegmentOnePass_DV_8u16s((Ipp8u *) lpSrc,
                                                       (Ipp32u *) _INTERNAL_DEZIGZAG_TABLE_0,
                                                       (Ipp32u *) m_pHuffTable,
                                                       (Ipp16s *) lpsBlocks,
                                                       (Ipp32u *) BlParamBuffer,
                                                       64);
                else
                    ippiHuffmanDecodeSegment_DV_8u16s((Ipp8u *) lpSrc,
                                                      (Ipp32u *) _INTERNAL_DEZIGZAG_TABLE_0,
                                                      (Ipp32u *) m_pHuffTable,
                                                      (Ipp16s *) lpsBlocks,
                                                      (Ipp32u *) BlParamBuffer);

                // do dequantize and iDCT
                for (mb_num = 0;mb_num < 5;mb_num++)
                {
                    // get quantization number
                    qno = (BlParamBuffer[mb_num * 6] >> 16) & 0x0F;

                    // decompress each block
                    for (b_num = 0;b_num < 6;b_num++)
                    {
                        block_quant_class = (BlParamBuffer[mb_num * 6 + b_num] >> 4) & 0x03;
                        block_type = (BlParamBuffer[mb_num * 6 + b_num] >> 6) & 0x01;
                        bl_index = (BlParamBuffer[mb_num * 6 + b_num] >> 8) & 0xff;
                        eob = BlParamBuffer[mb_num * 6 + b_num]  & 0x01;

                                    // get beginning of arrays of dequantize tables
                        lpsTable =  m_lpADequantizeTable +
                                    // get needed array of tables, depending on block class
                                    (block_type * 64 * 14) +
                                    // get offset of needed table, depending on quantization class
                                    // & quantization number
                                    64 * m_lpADequantizeLineTable[qno + block_quant_class * 16];

                        // do inverse and adaptive dequantization
#ifdef FAST_DV
                        // fast but not exact quantization of DV
                        ippiQuantInv_DV_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num + 6 * mb_num)), (Ipp16s *) lpsTable);
#else
                        Ipp16s *wT;
                        if (0 == block_type)
                            wT = (Ipp16s *)TablW0_Scale_ZigZag;
                        else
                            wT = (Ipp16s *)TablW1_Scale_ZigZag;
                        // exact quantization of DV / DV50
                        ippiQuantWeightBlockInv_DV_16s_C1I(
                            (Ipp16s *) (lpsBlocks + 64 * (b_num + 6 * mb_num)),
                            (Ipp16s *) lpsTable,
                            wT );
#endif

                        // do iDCT
                        if (0 == block_type)
                        {
                            if(m_lSizeSubSampled == 2)
                                ippiDCT8x4x2To4x4Inv_DV_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num +6 * mb_num)));
                            else if ((eob == 0)||( 10 > bl_index))
                            {
                                ippiDCT8x8Inv_4x4_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num + 6 * mb_num)));
                            }
                            else
                                ippiDCT8x8Inv_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num + 6 * mb_num)));
                        }
                        else
                        {
                            if(m_lSizeSubSampled == 2)
                                ippiDCT8x4x2To4x4Inv_DV_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num +6 * mb_num)));
                            else
                                ippiDCT2x4x8Inv_16s_C1I((Ipp16s *) (lpsBlocks + 64 * (b_num +6 * mb_num)));
                        }
                    }
                }
                // store data to memory
                (this->*(StoreDVSegment))(i, k, nThreadNum);
            }
        }
    }
    else
    {
        for (i = i_start;i < i_stop;i += 1)
        {
            for (k = 0;k < 27;k += 1)
            {
                // get source pointer
                lpSrc = m_lpSource + SIZE_OF_DV_SEGMENT * (i * 150 + 6 + (k / 3 + 1) + k * 5);

                HuffmanDecodeSegment_DV_DC_only(lpSrc, (Ipp16u*)lpsBlocks);

                // store data to memory
                (this->*StoreDVSegment)(i, k, nThreadNum);
            }
        }
    }

} // void DVVideoDecoder::DecompressSegment(Ipp32u ThreadNum)

Status DVVideoDecoder::GetPerformance(Ipp64f *perf)
{
    if (perf)
        *perf = 1.0;

    return UMC_OK;

} // Status DVVideoDecoder::GetPerformance(Ipp64f *perf)

Status DVVideoDecoder::Reset(void)
{
    if (!m_bInitSuccess)
        return UMC_ERR_NOT_INITIALIZED;
    else
        return UMC_OK;

} // Status DVVideoDecoder::Reset(void)

const Ipp64f MINIMAL_DOUBLE_DIFFERENCE = 1.0/1000000.0;
inline
bool IsEqual(Ipp64f dOne, Ipp64f dTwo)
{
    if (fabs(dOne - dTwo) <= MINIMAL_DOUBLE_DIFFERENCE)
        return true;

    return false;

} //bool IsEqual(Ipp64f dOne, Ipp64f dTwo)

Ipp32u VM_THREAD_CALLCONVENTION DVVideoDecoder::ThreadWorkingRoutine(void *lpv)
{
    THREAD_ID *lpThreadId;
    DVVideoDecoder *lpOwner;

    // check error(s)
    if (NULL == lpv)
        return 0x0bad;

    lpThreadId = reinterpret_cast<THREAD_ID *> (lpv);
    lpOwner = reinterpret_cast<DVVideoDecoder *> (lpThreadId->m_lpOwner);

    // wait for start
    vm_event_wait(lpOwner->m_lpStartEvent + lpThreadId->m_nNumber);

    while (VM_TIMEOUT == vm_event_timed_wait(&(lpOwner->m_Quit), 0))
    {
        // decompress part of frame
        lpOwner->DecompressSegment(lpThreadId->m_nNumber);

        // set finish
        vm_event_signal(lpOwner->m_lpStopEvent + lpThreadId->m_nNumber);

        // wait for start
        vm_event_wait(lpOwner->m_lpStartEvent + lpThreadId->m_nNumber);
    };

    return lpThreadId->m_nNumber;

} // Ipp32u DVVideoDecoder::ThreadWorkingRoutine(void *lpv)

Status DVVideoDecoder::GetInfo(BaseCodecParams* info)
{
    VideoDecoderParams *pParams;
    if(info == NULL)
        return UMC_ERR_NULL_PTR;

    if (m_bInitSuccess == false)
        return UMC_ERR_NOT_INITIALIZED;

    pParams = DynamicCast<VideoDecoderParams> (info);

    if ((NULL != pParams) && (m_bInitSuccess == true) ) {
        pParams->info = m_ClipInfo;
        pParams->lpMemoryAllocator = m_pMemoryAllocator;
        pParams->pPostProcessing = m_PostProcessing;
        pParams->numThreads = m_nNumberOfThreads;
    }
    return UMC_OK;

} // Status DVVideoDecoder::GetInfo(void)

} // end namespace UMC

#endif // UMC_ENABLE_DV_VIDEO_DECODER
